<template>
  <div class="containers">
    <div style="background: #f8f8f8;">
      <div class="tools" style="">
        <span title="放大" @click="handleZoom(0.2)"><svg-icon icon-class="fd1" /></span>
        <span title="缩小" @click="handleZoom(-0.2)"><svg-icon icon-class="sx2" /></span>
        <span class="uploadSpan">
          <input ref="file" type="file" accept=".bpmn">
          <svg-icon icon-class="sc" />
        </span>
        <span title="预览xml" @click="openXMLPreviewModel"><svg-icon icon-class="yl" /></span>
        <span title="预览svg" @click="openXMLPreviewSVG"><svg-icon icon-class="svgimg" /></span>
        <a title="下载svg" href="javascript:" ref="savsSVG"><svg-icon style="color:#000;" icon-class="svg2"/></a>
        <a ref="saveDiagram" title="下载xml" href="javascript:"><svg-icon icon-class="xz" /></a>
        <!-- <a ref="clearHb" title="清空画布" href="javascript:"><svg-icon icon-class="qk" /></a> -->
      </div>
    </div>
    <div v-if="loading" class="loading">
      加载中...
    </div>
    <template v-else>
      <div ref="canvas" class="canvas"></div>
      <div class="properties-tool">
        <div id="js-properties-panel" class="panel"></div>
      </div>
    </template>

    <el-dialog
      title="XML数据预览"
      :visible.sync="dialogVisible"
      :close-on-click-modal="false"
      width="60%"
    >
      <div v-if="dialogVisible" style="padding: 10px;max-height:500px;overflow-y: scroll;">
        <pre
          v-highlight
          style="word-wrap: break-word;
          white-space:pre-wrap;"
          language="xml"
        >
          <code language="xml">{{ xmlInfo }}</code>
        </pre>
      </div>
      <span slot="footer" class="dialog-footer">
        <el-button type="primary" @click="dialogVisible = false">确 定</el-button>
      </span>
    </el-dialog>

      <div class="svgImgContent" @click="svgImgurlFlag=false" v-if="svgImgurlFlag">
        <img class="svgImg"   :src="svgImgurl" alt="">
      </div>
  </div>
</template>

<script>
// 引入相关的依赖
import BpmnModeler from 'bpmn-js/lib/Modeler'
import axios from 'axios'
import { xmlStr } from './xmlStr'
import propertiesProviderModule from 'bpmn-js-properties-panel-activiti/lib/provider/activiti'
import propertiesPanelModule from 'bpmn-js-properties-panel-activiti'
import activitiModdleDescriptor from './activiti' // 好像没用？

import Vue from 'vue'
import hljs from 'highlight.js'
// 汉化包
import customTranslate from './lib/customTranslate'
Vue.directive('highlight', function(el) {
  const blocks = el.querySelectorAll('pre code')
  blocks.forEach((block) => {
    hljs.highlightBlock(block)
  })
})
export default {
  name: '',
  data() {
    return {
      svgImgurlFlag:false,
      dialogVisible: false,
      xmlInfo: null,
      // bpmn建模器
      bpmnModeler: null,
      container: null,
      canvas: null,
      loading: true,
      svgImgurl:"",
      xmlUrl: '',
      defaultXmlStr: xmlStr,
      xmlData:'',
      scale: 1
    }
  },
  // 生命周期 - 载入后, Vue 实例挂载到实际的 DOM 操作完成，一般在该过程进行 Ajax 交互
  mounted() {
    this.init()
    this.$refs.file.onchange = () => {
      this.loadFile()
    }
  },
  // 方法集合
  methods: {
    handleZoom(flag) {
      // 放大缩小倍数flag可以自行设置
      if (flag < 0 && this.scale <= 1) {
        return
      }
      this.scale += flag
      this.$nextTick(() => {
        this.bpmnModeler.get('canvas').zoom(this.scale)
      })
    },
    // 初始化函数
    async init() {
      this.loading = true
      this.xmlUrl = await this.getXmlUrl()

      this.loading = false
      this.$nextTick(() => {
        this.bpmnModeler && this.bpmnModeler.destroy() // 加载前先销毁
        this.initBpmn()
      })
    },
    getXmlUrl() {
      return new Promise(resolve => {
        setTimeout(() => {
          // const url = 'https://hexo-blog-1256114407.cos.ap-shenzhen-fsi.myqcloud.com/mock1.bpmn'
          const url = ''
          resolve(url)
        }, 1000)
      })
    },
    // 页面加载完调用
    initBpmn() {
      // 获取到属性ref为“canvas”的dom节点
      const canvas = this.$refs.canvas
      const customTranslateModule = {
        translate: ['value', customTranslate]
      }
      // 建模
      this.bpmnModeler = new BpmnModeler({
        container: canvas,
        propertiesPanel: {// 添加控制板
          parent: '#js-properties-panel'
        },
        additionalModules: [
          propertiesProviderModule, // 左边工具栏以及节点
          propertiesPanelModule, // 右边的工具栏
          customTranslateModule // 汉化
        ],
        moddleExtensions: {
          activiti: activitiModdleDescriptor
        }
      })
      this.createNewDiagram()
    },
    async createNewDiagram() {
      const that = this
      let bpmnXmlStr = ''
      let strxml = this.$route.query
      if (strxml&&strxml.xml) { // 如果没有默认的就使用本地的默认数据  this.xmlUrl === ''
        this.transformCanvas(strxml.xml)
      } else {
        bpmnXmlStr = this.defaultXmlStr
        this.transformCanvas(bpmnXmlStr)
      }
    },
    // 将字符串转换成图并渲染
    transformCanvas(bpmnXmlStr) {
      this.bpmnModeler.importXML(bpmnXmlStr, (err) => {
        if (err) {
          console.error(err)
        } else {
          this.success()
        }
        // 让图能自适应屏幕
        var canvas = this.bpmnModeler.get('canvas')
        canvas.zoom('fit-viewport')
      })
    }, 
    openXMLPreviewSVG(){
      this.bpmnModeler.saveSVG({ format: true }, (err, xml) => {
        if (err) {
          this.$message.error('预览失败')
          return console.error('could not save BPMN 2.0 diagram', err)
        }
        let srcVal = window.btoa(unescape(encodeURIComponent(xml)))
        this.svgImgurl = "data:image/svg+xml;base64," + srcVal 
        this.svgImgurlFlag = true
      })
    },
    // 预览xml
    openXMLPreviewModel() {
      this.bpmnModeler.saveXML({ format: true }, (err, xml) => {
        if (err) {
          this.$message.error('预览失败')
          return console.error('could not save BPMN 2.0 diagram', err)
        }
        this.dialogVisible = true
        this.xmlInfo = xml
      })
    },
    async downloadProcess(type, name = 'diagram') {
      try {
        // 按需要类型创建文件并下载
        if (type === 'xml' || type === 'bpmn') {
          const { err, xml } = await modeler.saveXML()
          // 读取异常时抛出异常
          if (err) {
            console.error(`[Process Designer Warn ]: ${err.message || err}`)
          }
          const { href, filename } = this.setEncoded(type.toUpperCase(), name, xml)
          downloadFile(href, filename)
        } else {
          const { err, svg } = await modeler.saveSVG()
          // 读取异常时抛出异常
          if (err) {
            return console.error(err)
          }
          const { href, filename } = this.setEncoded('SVG', name, svg)
          downloadFile(href, filename)
        }
      } catch (e) {
        console.error(`[Process Designer Warn ]: ${e.message || e}`)
      }
    },
    downloadXML(xml) {
      const blob = new Blob([xml], { type: 'text/xml' })
      const link = document.createElement('a')
      link.href = URL.createObjectURL(blob)
      link.download = 'bpmn.xml'
      link.click()
    },
    // 下载xml文件？

    loadFile() {
      const _this = this
      // 获取input标签选择的文件,并选择第一条
      const resultFile = this.$refs.file.files[0]
      // 如果文件存在
      if (resultFile) {
        // 使用 FileReader 来读取文件
        const reader = new FileReader()
        // 读取纯文本文件,且编码格式为 utf-8
        reader.readAsText(resultFile, 'UTF-8')
        // 读取文件,会触发 onload 异步事件,可使用回调函数 来获取最终的值.
        reader.onload = e => {
          _this.defaultXmlStr = e.target.result
          this.createNewDiagram()
        }
      }
    },
    success() {
      console.log('创建成功!')
      this.addBpmnListener()
      this.addModelerListener()
    },
    // 添加绑定事件
    addBpmnListener() {
      const that = this
      // 获取a标签dom节点
      const downloadLink = this.$refs.saveDiagram
      const savsSVG = this.$refs.savsSVG
      // 给图绑定事件，当图有发生改变就会触发这个事件
      this.bpmnModeler.on('commandStack.changed', function() {
        that.saveDiagram(function(err, xml) {
          that.setEncoded(downloadLink, 'diagram.bpmn', err ? null : xml)
        })
        that.saveDiagram2(function (err, xml) {
          that.setEncoded(savsSVG, 'diagram.svg', err ? null : xml)
        })
      })
    },
    saveDiagram2(done) {
      // 把传入的done再传给bpmn原型的saveXML函数调用
      this.bpmnModeler.saveSVG({format: true}, function (err, xml) {
        done(err, xml) //下载svg调用
      })
    },
    addModelerListener() {
      // 监听 modeler
      const bpmnjs = this.bpmnModeler
      const that = this
      // 'shape.removed'
      const events = ['shape.added', 'shape.move.end', 'connect.end', 'connection.create', 'connection.move']
      events.forEach(function(event) {
        that.bpmnModeler.on(event, e => {
          var elementRegistry = bpmnjs.get('elementRegistry')
          var shape = e.element ? elementRegistry.get(e.element.id) : e.shape
        })
      })
    },
    // 下载为SVG格式,done是个函数，调用的时候传入的
    saveSVG(done) {
      // 把传入的done再传给bpmn原型的saveSVG函数调用
      this.bpmnModeler.saveSVG(done)
    },
    // 下载为bpmn格式,done是个函数，调用的时候传入的
    saveDiagram(done) {
      // 把传入的done再传给bpmn原型的saveXML函数调用
      this.bpmnModeler.saveXML({ format: true }, function(err, xml) {
        done(err, xml)
      })
    },
    // 当图发生改变的时候会调用这个函数，这个data就是图的xml
    setEncoded(link, name, data) {
      // 把xml转换为URI，下载要用到的
      const encodedData = encodeURIComponent(data)
      // 下载图的具体操作,改变a的属性，className令a标签可点击，href令能下载，download是下载的文件的名字
      const xmlFile = new File([data], 'test.bpmn')
      if (data) {
        link.className = 'active'
        link.href = 'data:application/bpmn20-xml;charset=UTF-8,' + encodedData
        // window.open(link.href)
        link.download = name
      }
    }
  }
}
</script>

<style lang="scss" scoped>
 /*左边工具栏以及编辑节点的样式*/
@import '~bpmn-js/dist/assets/diagram-js.css';
@import '~bpmn-js/dist/assets/bpmn-font/css/bpmn.css';
@import '~bpmn-js/dist/assets/bpmn-font/css/bpmn-codes.css';
@import '~bpmn-js/dist/assets/bpmn-font/css/bpmn-embedded.css';

  input[type='file']{

  }
  .svgImgContent{
    z-index: 9999;
    position: fixed;
    left:0px;
    top:0px;
    width: 100%;
    background: rgba(0,0,0,.8);
    display: flex;
    align-items: center;
    justify-content: center;
    height: 100vh; /* 设置容器的高度，可以根据实际情况调整 */
    img{
      padding: 20px;
      background: #fff;
      width:50%;
      height:50%;
    }
  }
  >>>.el-dialog__header {
    padding: 16px 16px 8px 16px;
    box-sizing: border-box;
    border-bottom: 1px solid #e8e8e8;
}
  .tools{
    display: flex;
    width: 75%;
    padding-left:10px;
    span,a{
      display: inline-block;
      padding:5px 10px;
      border-right: 1px solid #fff;
      border-left: 1px solid #e7e6e6;
      cursor: pointer;
      svg{
        color: #040000;
        font-size: 16px;
        display: block;
        margin: 0 auto;
      }
    }
    span:first-child{
      border-left:none;
    }
    span:last-child{
      border-right:none;
    }
    .uploadSpan{
      position: relative;
      width:38px;
      text-align: center;
      input,svg{
        position: absolute;
        left: 0;
        width: 28px;
        top: 0;
      }
      svg{
        margin: 4px 0 0 4px;
        z-index: 0;
      }
      input{
        opacity: 0;
        width: 38px;
        height: 100%;
        z-index: 100;
      }
    }
  }
// 滚动条样式
>>>::-webkit-scrollbar{
    display: none;
}
>>>.bjs-powered-by {
    display: none;
  }

.loading {
  font-size: 26px;
  text-align: center;
  margin-top: 260px;
}

.containers {
  background-color: #ffffff;
  padding: 10px 0;
  width: 100%;
  height: calc(100vh - 52px);
}

.canvas {
  width: 100%;
  height: 100%;
  background: url("../../../../assets/images/bgc.png") no-repeat left top;
}

.panel {
  position: absolute;
  right: 0;
  top: 0;
  width: 100%;
}

>>>.properties-tool {
  position: fixed;
  width:300px;
  height: 100%;
  right: 0;
  top:116px;
  overflow: auto;
}

.buttons {
  position: absolute;
  left: 20px;
  bottom: 20px;
}

.buttons li {
  display: inline-block;
  margin: 5px;
}

.buttons li a {
  color: #999;
  background: #eee;
  cursor: not-allowed;
  padding: 8px;
  border: 1px solid #ccc;
  text-decoration: none;
}

.buttons li a.active {
  color: #333;
  background: #fff;
  cursor: pointer;
}
/* -------- */
>>>.bpp-properties-panel {
    background-color: #f8f8f8;
    position: relative;
    border-left: 2px solid #2778bd;
}
>>>.bpp-properties-header {
    padding: 15px;
    padding-bottom: 5px;
    background: #2778bd;
    color: #fff;
}
>>>.bpp-properties-group:hover > .group-toggle:hover {
    background-color: none;
}
>>>.bpp-properties-group:hover > .group-toggle {
    background-color: #ccc;
}
>>>.bpp-field-description{
  font-size: 12px;
}
>>>.bpp-properties-panel label {
    font-weight:400;
    display: inline-block;
    vertical-align: middle;
    color: #666;
    margin-bottom: 3px;
}
>>>ul.bpp-properties-tabs-links > li.bpp-active a {
    padding-bottom: 5px;
    border-top: 2px solid #fc9001;
    border-bottom: none;
}
.bpp-properties-tab-bar {
    border-bottom: 1px solid #ccc;
    padding: 0 15px;
}
// 覆盖
>>>.bpp-properties-group > .group-label {
    font-weight: bolder;
    display: inline-block;
    vertical-align: middle;
    color: #2778bd;
    font-size: 14px;
    margin-top:0;
    margin-bottom:0;
    -webkit-transition: margin 0.218s linear;
    transition: margin 0.218s linear;
    font-style: inherit;
}
>>>input[type='text']{
  -webkit-appearance: none;
    background-color: #fff;
    background-image: none;
    border-radius: 4px;
    border: 1px solid #dcdfe6;
    -webkit-box-sizing: border-box;
    box-sizing: border-box;
    color: #606266;
    display: inline-block;
    font-size: inherit;
    height: 32px;
    line-height:32px;
    outline: none;
    padding: 0 15px;
    -webkit-transition: border-color .2s cubic-bezier(0.645, 0.045, 0.355, 1);
    transition: border-color .2s cubic-bezier(0.645, 0.045, 0.355, 1);
    width: 100%;
}
</style>
